<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>工作流编辑器 - 优化版</title>
    <link rel="stylesheet" href="js/reactflow.css">
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        
        body { 
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 
            background-color: #f9f9f9;
        }
        
        .toolbar { 
            height: 60px; 
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            padding: 0 20px; 
            display: flex; 
            align-items: center; 
            gap: 15px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        
        .toolbar button { 
            background: rgba(255,255,255,0.2);
            border: 1px solid rgba(255,255,255,0.3);
            color: white;
            padding: 8px 16px; 
            border-radius: 6px; 
            cursor: pointer;
            font-size: 13px;
            transition: all 0.2s ease;
            backdrop-filter: blur(10px);
        }
        
        .toolbar button:hover { 
            background: rgba(255,255,255,0.3);
            transform: translateY(-1px);
        }
        
        .toolbar button:active { 
            transform: translateY(0);
        }
        
        .split { 
            display: flex; 
            height: calc(100vh - 60px); 
        }
        
        .split > div:first-child { 
            flex: 1; 
            background: #fff;
            border-right: 1px solid #e1e5e9;
        }
        
        .props { 
            width: 320px; 
            padding: 20px; 
            background: #fafbfc;
            overflow-y: auto;
            box-shadow: -2px 0 8px rgba(0,0,0,0.05);
        }
        
        .props label { 
            display: block; 
            margin: 12px 0 4px; 
            font-weight: 600;
            color: #2c3e50;
            font-size: 13px;
        }
        
        .props input, 
        .props textarea { 
            width: 100%; 
            padding: 10px 12px; 
            border: 1px solid #ddd; 
            border-radius: 6px; 
            font-size: 13px;
            transition: border-color 0.2s ease;
            background: white;
        }
        
        .props input:focus, 
        .props textarea:focus { 
            outline: none; 
            border-color: #667eea;
            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
        }
        
        .props textarea { 
            resize: vertical; 
            line-height: 1.4;
        }
        
        .node-header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 15px;
            margin: -20px -20px 20px -20px;
            border-radius: 0;
            font-weight: 600;
            font-size: 14px;
        }
        
        .stats {
            color: rgba(255,255,255,0.9);
            font-size: 12px;
            font-weight: normal;
        }
        
        .help-section {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 6px;
            margin-top: 20px;
            border: 1px solid #e9ecef;
        }
        
        .help-section h4 {
            color: #495057;
            margin-bottom: 10px;
            font-size: 13px;
        }
        
        .help-section ul {
            list-style: none;
            padding: 0;
        }
        
        .help-section li {
            padding: 4px 0;
            font-size: 12px;
            color: #6c757d;
            position: relative;
            padding-left: 16px;
        }
        
        .help-section li:before {
            content: "•";
            position: absolute;
            left: 0;
            color: #667eea;
        }
        
        .error-message {
            background: #fee;
            color: #d63031;
            padding: 8px 12px;
            border-radius: 4px;
            font-size: 12px;
            margin-top: 8px;
            border: 1px solid #fab1a0;
        }
        
        .position-info {
            background: #e8f4fd;
            color: #0984e3;
            padding: 8px 12px;
            border-radius: 4px;
            font-size: 12px;
            margin-top: 15px;
            border: 1px solid #74b9ff;
        }

        /* ReactFlow 自定义样式 */
        .react-flow__node {
            border-radius: 8px;
            box-shadow: 0 2px 12px rgba(0,0,0,0.1);
            transition: box-shadow 0.2s ease;
        }
        
        .react-flow__node:hover {
            box-shadow: 0 4px 20px rgba(0,0,0,0.15);
        }
        
        .react-flow__node.selected {
            box-shadow: 0 4px 20px rgba(102, 126, 234, 0.3);
        }
        
        .react-flow__edge-path {
            stroke-width: 2;
            stroke: #667eea;
        }
        
        .react-flow__controls {
            box-shadow: 0 2px 12px rgba(0,0,0,0.1);
        }
        
        .react-flow__controls button {
            border: 1px solid #ddd;
            background: white;
        }
        
        .react-flow__controls button:hover {
            background: #f8f9fa;
        }
    </style>
</head>
<body>
    <div id="root"></div>
    
    <script src="js/react.development.js"></script>
    <script src="js/react-dom.development.js"></script>
    <script src="js/babel.min.js"></script>
    <script src="js/reactflow.js"></script>
    
    <script type="text/babel">
        const { useState, useEffect, useCallback, useRef, useMemo } = React;
        const { ReactFlowProvider, ReactFlow, Controls, Background, addEdge, applyNodeChanges, applyEdgeChanges } = window.ReactFlow;

        /* ------------ 常量定义 ------------ */
        const STORAGE_KEY = 'workflow-data';
        const DEFAULT_NODE_STYLE = {
            background: '#fff',
            border: '2px solid #667eea',
            borderRadius: '8px',
            padding: '12px',
            fontSize: '13px',
            fontWeight: '500',
            color: '#2c3e50'
        };

        /* ------------ 初始空配置 ------------ */
        const emptyFlow = {
            Cmds: [
                { Name: 'Start', Cmd: 'Start', Args: { Type: 'Start', 'Next.0': 'Node2' }, position: { x: 50, y: 50 } },
                { Name: 'Node2', Cmd: 'DeviceIntentionVoice', Args: { Type: 'Normal', 'Next.0': 'End' }, position: { x: 250, y: 50 } },
                { Name: 'End', Cmd: 'Echo', Args: { Type: 'End' }, position: { x: 450, y: 50 } }
            ]
        };

        /* ------------ 工具函数 ------------ */
        const loadFile = () => {
            try {
                const data = localStorage.getItem(STORAGE_KEY);
                return data ? JSON.parse(data) : emptyFlow;
            } catch (error) {
                console.error('Failed to load data from localStorage:', error);
                return emptyFlow;
            }
        };

        const saveFile = (data) => {
            try {
                localStorage.setItem(STORAGE_KEY, JSON.stringify(data, null, 2));
                console.log('Data saved to localStorage');
            } catch (error) {
                console.error('Failed to save data to localStorage:', error);
            }
        };

        /* ------------ 自定义Hook：数据转换 ------------ */
        const useFlowData = () => {
            const convertToReactFlow = useCallback((cfg) => {
                const nodes = cfg.Cmds.map(c => ({
                    id: c.Name,
                    type: 'default',
                    position: c.position || { x: 0, y: 0 },
                    data: {
                        ...c,
                        label: c.Name,
                        style: DEFAULT_NODE_STYLE
                    }
                }));

                const edges = [];
                cfg.Cmds.forEach(c => {
                    Object.keys(c.Args || {})
                        .filter(k => k.startsWith('Next.'))
                        .forEach(k => {
                            const targetId = c.Args[k];
                            if (targetId && cfg.Cmds.some(cmd => cmd.Name === targetId)) {
                                edges.push({
                                    id: `${c.Name}-${targetId}`,
                                    source: c.Name,
                                    target: targetId,
                                    animated: true,
                                    style: { strokeWidth: 2, stroke: '#667eea' }
                                });
                            }
                        });
                });

                return { nodes, edges };
            }, []);

            const convertFromReactFlow = useCallback((nodes) => {
                const cfg = { Cmds: [] };
                nodes.forEach(n => {
                    if (n.data.Name && n.data.Cmd) {
                        cfg.Cmds.push({
                            Name: n.data.Name || n.id,
                            Cmd: n.data.Cmd,
                            Args: n.data.Args || {},
                            position: n.position
                        });
                    }
                });
                return cfg;
            }, []);

            return { convertToReactFlow, convertFromReactFlow };
        };

        /* ------------ 主组件 ------------ */
        function App() {
            const [nodes, setNodes] = useState([]);
            const [edges, setEdges] = useState([]);
            const [selected, setSelected] = useState(null);
            const [jsonError, setJsonError] = useState('');
            const fileInputRef = useRef(null);

            const { convertToReactFlow, convertFromReactFlow } = useFlowData();

            // 计算当前配置
            const currentConfig = useMemo(() => convertFromReactFlow(nodes), [nodes, convertFromReactFlow]);

            /* 初始化数据 */
            useEffect(() => {
                const initialData = loadFile();
                const { nodes: initialNodes, edges: initialEdges } = convertToReactFlow(initialData);
                setNodes(initialNodes);
                setEdges(initialEdges);
            }, [convertToReactFlow]);

            /* 自动保存（防抖） */
            useEffect(() => {
                if (nodes.length === 0) return;

                const timeoutId = setTimeout(() => {
                    saveFile(currentConfig);
                }, 1000); // 1秒防抖

                return () => clearTimeout(timeoutId);
            }, [currentConfig]);

            /* 文件操作 */
            const handleImport = useCallback((e) => {
                const file = e.target.files[0];
                if (!file) return;

                const reader = new FileReader();
                reader.onload = (event) => {
                    try {
                        const config = JSON.parse(event.target.result);
                        if (!config.Cmds || !Array.isArray(config.Cmds)) {
                            throw new Error('Invalid file format: missing Cmds array');
                        }
                        const { nodes: newNodes, edges: newEdges } = convertToReactFlow(config);
                        setNodes(newNodes);
                        setEdges(newEdges);
                        setSelected(null);
                        alert(`成功导入 ${config.Cmds.length} 个节点`);
                    } catch (error) {
                        alert('导入失败：JSON格式错误或文件格式不正确');
                        console.error('Import error:', error);
                    }
                };
                reader.readAsText(file);
                e.target.value = ''; // 重置input，允许重复导入同一文件
            }, [convertToReactFlow]);

            const handleExport = useCallback(() => {
                const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-');
                const blob = new Blob([JSON.stringify(currentConfig, null, 2)], {
                    type: 'application/json'
                });
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = `workflow-${timestamp}.json`;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
            }, [currentConfig]);

            /* ReactFlow 回调 */
            const onNodesChange = useCallback((changes) => {
                setNodes(nodes => applyNodeChanges(changes, nodes));
            }, []);

            const onEdgesChange = useCallback((changes) => {
                setEdges(edges => applyEdgeChanges(changes, edges));
            }, []);

            const onConnect = useCallback((params) => {
                setEdges(edges => addEdge({ ...params, animated: true, style: { strokeWidth: 2, stroke: '#667eea' } }, edges));
            }, []);

            /* 节点选择和编辑 */
            const onNodeClick = useCallback((_, node) => {
                setSelected(node);
                setJsonError('');
            }, []);

            const updateNodeField = useCallback((field, value) => {
                if (!selected) return;

                setNodes(nodes =>
                    nodes.map(n => {
                        if (n.id === selected.id) {
                            const updatedNode = {
                                ...n,
                                data: { ...n.data, [field]: value }
                            };
                            // 如果更新的是Name字段，同时更新id和label
                            if (field === 'Name') {
                                updatedNode.id = value;
                                updatedNode.data.label = value;
                            }
                            return updatedNode;
                        }
                        return n;
                    })
                );

                // 更新selected状态
                setSelected(prev => {
                    const updatedSelected = {
                        ...prev,
                        data: { ...prev.data, [field]: value }
                    };
                    if (field === 'Name') {
                        updatedSelected.id = value;
                        updatedSelected.data.label = value;
                    }
                    return updatedSelected;
                });

                // 如果更新的是Name字段，同时更新相关的edges
                if (field === 'Name') {
                    setEdges(edges =>
                        edges.map(edge => ({
                            ...edge,
                            source: edge.source === selected.id ? value : edge.source,
                            target: edge.target === selected.id ? value : edge.target,
                            id: edge.source === selected.id ? `${value}-${edge.target}` :
                                edge.target === selected.id ? `${edge.source}-${value}` : edge.id
                        }))
                    );
                }
            }, [selected]);

            const updateNodeArgs = useCallback((jsonString) => {
                setJsonError('');
                try {
                    const args = JSON.parse(jsonString);
                    updateNodeField('Args', args);
                } catch (error) {
                    setJsonError('JSON格式错误：请检查括号、引号和逗号是否正确');
                }
            }, [updateNodeField]);

            /* 添加新节点 */
            const addNewNode = useCallback(() => {
                const newId = `Node_${Date.now()}`;
                const newNode = {
                    id: newId,
                    type: 'default',
                    position: {
                        x: Math.random() * 400 + 100,
                        y: Math.random() * 300 + 100
                    },
                    data: {
                        Name: newId,
                        Cmd: 'NewCommand',
                        Args: { Type: 'Normal' },
                        label: newId,
                        style: DEFAULT_NODE_STYLE
                    }
                };
                setNodes(nodes => [...nodes, newNode]);
                setSelected(newNode); // 自动选中新添加的节点
            }, []);

            /* 删除选中节点 */
            const deleteSelectedNode = useCallback(() => {
                if (!selected) return;
                
                const confirmed = window.confirm(`确定删除节点 "${selected.data.Name || selected.id}" 吗？`);
                if (!confirmed) return;

                setNodes(nodes => nodes.filter(n => n.id !== selected.id));
                setEdges(edges => edges.filter(e => e.source !== selected.id && e.target !== selected.id));
                setSelected(null);
            }, [selected]);

            /* 清空所有节点 */
            const clearAll = useCallback(() => {
                const confirmed = window.confirm('确定清空所有节点吗？此操作不可撤销。');
                if (!confirmed) return;

                setNodes([]);
                setEdges([]);
                setSelected(null);
            }, []);

            return (
                <>
                    <div className="toolbar">
                        <input
                            type="file"
                            accept=".json"
                            ref={fileInputRef}
                            onChange={handleImport}
                            style={{ display: 'none' }}
                        />
                        <button onClick={() => fileInputRef.current?.click()}>
                            📁 导入 JSON
                        </button>
                        <button onClick={handleExport}>
                            💾 导出 JSON
                        </button>
                        <button onClick={addNewNode}>
                            ➕ 添加节点
                        </button>
                        {selected && (
                            <button onClick={deleteSelectedNode} style={{ background: 'rgba(255,59,48,0.2)', borderColor: 'rgba(255,59,48,0.3)' }}>
                                🗑️ 删除节点
                            </button>
                        )}
                        <button onClick={clearAll} style={{ background: 'rgba(255,149,0,0.2)', borderColor: 'rgba(255,149,0,0.3)' }}>
                            🧹 清空所有
                        </button>
                        <div className="stats">
                            节点数: {nodes.length} | 连接数: {edges.length}
                        </div>
                    </div>

                    <div className="split">
                        <ReactFlowProvider>
                            <ReactFlow
                                nodes={nodes}
                                edges={edges}
                                onNodesChange={onNodesChange}
                                onEdgesChange={onEdgesChange}
                                onConnect={onConnect}
                                onNodeClick={onNodeClick}
                                fitView
                                attributionPosition="top-right"
                                proOptions={{ hideAttribution: true }}
                                defaultViewport={{ x: 0, y: 0, zoom: 1 }}
                                minZoom={0.2}
                                maxZoom={2}
                                snapToGrid={true}
                                snapGrid={[15, 15]}
                            >
                                <Controls showZoom={true} showFitView={true} showInteractive={true} />
                                <Background variant="dots" gap={15} size={1} color="#e1e5e9" />
                            </ReactFlow>
                        </ReactFlowProvider>

                        <div className="props">
                            {selected ? (
                                <>
                                    <div className="node-header">
                                        编辑节点：{selected.data.Name || selected.id}
                                    </div>

                                    <label>节点名称</label>
                                    <input
                                        value={selected.data.Name || selected.id}
                                        onChange={e => updateNodeField('Name', e.target.value)}
                                        placeholder="输入节点名称"
                                    />

                                    <label>命令类型</label>
                                    <input
                                        value={selected.data.Cmd || ''}
                                        onChange={e => updateNodeField('Cmd', e.target.value)}
                                        placeholder="输入命令类型"
                                    />

                                    <label>参数 (JSON格式)</label>
                                    <textarea
                                        rows={10}
                                        value={JSON.stringify(selected.data.Args || {}, null, 2)}
                                        onChange={e => updateNodeArgs(e.target.value)}
                                        placeholder="输入JSON格式的参数"
                                        style={{
                                            fontFamily: 'Monaco, Consolas, "Courier New", monospace',
                                            fontSize: '12px',
                                            border: jsonError ? '1px solid #ff4d4f' : '1px solid #ddd'
                                        }}
                                    />
                                    {jsonError && (
                                        <div className="error-message">
                                            {jsonError}
                                        </div>
                                    )}

                                    <div className="position-info">
                                        位置: ({Math.round(selected.position?.x || 0)}, {Math.round(selected.position?.y || 0)})
                                    </div>
                                </>
                            ) : (
                                <div>
                                    <div className="node-header">
                                        工作流编辑器
                                    </div>
                                    <p>点击节点查看/编辑属性</p>
                                    <div className="help-section">
                                        <h4>快捷操作：</h4>
                                        <ul>
                                            <li>拖拽节点移动位置</li>
                                            <li>从节点边缘拖拽创建连接</li>
                                            <li>选中节点按Delete删除</li>
                                            <li>鼠标滚轮缩放视图</li>
                                            <li>拖拽空白区域平移视图</li>
                                            <li>双击空白区域适应视图</li>
                                        </ul>
                                    </div>
                                    <div className="help-section">
                                        <h4>节点参数格式：</h4>
                                        <ul>
                                            <li>使用 "Next.0": "NodeName" 创建连接</li>
                                            <li>Type 字段用于定义节点类型</li>
                                            <li>所有参数必须使用双引号</li>
                                            <li>JSON 格式必须严格正确</li>
                                        </ul>
                                    </div>
                                </div>
                            )}
                        </div>
                    </div>
                </>
            );
        }

        ReactDOM.createRoot(document.getElementById('root')).render(<App />);
    </script>
</body>
</html>
